package com.fadis.cmd;

import com.fadis.data.FString;
import com.fadis.FadisException;
import com.fadis.data.base.FadisString;
import com.fadis.expire.ExpireMgmr;
import com.fadis.protocol.*;
import com.fadis.db.FadisDB;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CmdString {

    public static final class set implements CmdHandler{

        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            String value = frame.getArg(2);

            long ex = -999;
            long px = -999;
            String existOption = "";

            int optionScanStart = 3;
            String arg = frame.getArg(optionScanStart);
            while (arg != null){
                if(arg.equalsIgnoreCase("ex")){
                    ex = -998; // 进入等待状态
                }else if(arg.equalsIgnoreCase("px")){
                    px = -998;
                }else if(arg.equalsIgnoreCase("nx")){
                    existOption = "nx";
                }else if(arg.equalsIgnoreCase("xx")){
                    existOption = "xx";
                }else{
                    if(ex == -998 || px == -998){
                        try {
                            long time = Long.parseLong(arg);
                            if(ex == -998) ex = time;
                            else if(px == -998) px = time;
                        }catch (NumberFormatException e){
                            throw new FadisException(4001, "期望的是一个long类型：" + arg);
                        }
                    }else{
                        throw new FadisException(4002, "未知的命令选项：" + arg);
                    }

                }
                optionScanStart += 1;
                arg = frame.getArg(optionScanStart);
            }

            if(ex == -998 || px == -998){
                throw new FadisException(4002, "命令不全，expect a 数字");
            }

            if(existOption.equals("nx") && FadisDB.containsKey(key)){
                // 不存在才设置
                return CmdEncoder.getDefault().encode(new BulkString());
            }
            if(existOption.equals("xx") && !FadisDB.containsKey(key)){
                // 存在才设置
                return CmdEncoder.getDefault().encode(new BulkString());
            }

            ExpireMgmr.deleteExpireInfo(key);
            FadisString str = FadisDB.tryToGetOrCreateString(key, true);
            str.setStr(value);

            if(ex > 0 || px > 0){
                long timeMilli = (ex > 0) ? ex * 1000 : px;
                ExpireMgmr.setExpireTime(key, timeMilli);
            }
            return CmdEncoder.getDefault().encode(new SimpleString("OK"));
        }
    }

    public static final class get implements CmdHandler{

        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);

            FadisString v = FadisDB.tryToGetOrCreateString(key, false);
            return CmdEncoder.getDefault().encode(new BulkString(v));
        }
    }

    public static final class setnx implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            String value = frame.getArg(2);
            if(FadisDB.containsKey(key)){
                return LongNumber.encodeLong(0);
            }else{
                FadisString str = FadisDB.tryToGetOrCreateString(key, true);
                ExpireMgmr.deleteExpireInfo(key);
                str.setStr(value);
                return LongNumber.encodeLong(1);
            }
        }
    }

    public static final class setex implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            String secondsStr = frame.getArg(2);
            String value = frame.getArg(3);

            long seconds = Long.parseLong(secondsStr);

            FadisString str = FadisDB.tryToGetOrCreateString(key, true);
            ExpireMgmr.deleteExpireInfo(key);
            str.setStr(value);

            ExpireMgmr.setExpireTime(key, seconds * 1000);
            return SimpleString.encodedSimpleString("OK");
        }
    }

    public static final class psetex implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            String millisecondsStr = frame.getArg(2);
            String value = frame.getArg(3);

            long milliseconds = Long.parseLong(millisecondsStr);

            FadisString str = FadisDB.tryToGetOrCreateString(key, true);
            ExpireMgmr.deleteExpireInfo(key);
            str.setStr(value);

            ExpireMgmr.setExpireTime(key, milliseconds);
            return SimpleString.encodedSimpleString("OK");
        }
    }

    public static final class getset implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            String value = frame.getArg(2);

            boolean contains = FadisDB.containsKey(key);
            FadisString str = FadisDB.tryToGetOrCreateString(key, true);
            String oldValue = str.getStr();
            ExpireMgmr.deleteExpireInfo(key);
            str.setStr(value);

            return BulkString.encodedBulkString( contains ? oldValue : null);

        }
    }

    public static final class strlen implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            FadisString v = FadisDB.tryToGetOrCreateString(key, false);

            int result = FString.strlen(v);
            return LongNumber.encodeLong(result);
        }
    }

    public static final class append implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            String key = frame.getArg(1);
            String value = frame.getArg(2);

            FadisString v = FadisDB.tryToGetOrCreateString(key, true);
            String s = FString.append(v.getStr(), value);
            v.setStr(s);

            return LongNumber.encodeInt(s.length());
        }
    }

    public static final class setrange implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            String offsetStr = frame.getArg(2);
            String value = frame.getArg(3);

            int offset = Integer.parseInt(offsetStr);

            FadisString str = FadisDB.tryToGetOrCreateString(key, true);

            String s = FString.setrange(str.getStr(), (int) offset, value);
            str.setStr(s);

            return LongNumber.encodeLong(s.length());

        }
    }

    public static final class getrange implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            String start = frame.getArg(2);
            String end = frame.getArg(3);

            long startOffset = Long.parseLong(start);
            long endOffset = Long.parseLong(end);

            FadisString str = FadisDB.tryToGetOrCreateString(key, false);
            if(str == null) return BulkString.encodedNull();
            String result = FString.getrange(str.getStr(), startOffset, endOffset);
            return BulkString.encodedBulkString(result);
        }
    }

    public static final class incr implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            long increment = 1;

            long r = FadisDB.tryToGetOrCreateLong(key, true);
            r += increment;
            FadisDB.put(key, new FadisString(r + ""));
            return LongNumber.encodeLong(r);
        }
    }

    public static final class incrby implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            String incrementStr = frame.getArg(2);

            long increment = 0;

            try {
                increment = Long.parseLong(incrementStr);
            }catch (Exception e){
                throw new FadisException(4002, increment + " 无法转为long");
            }

            long r = FadisDB.tryToGetOrCreateLong(key, true);
            r += increment;
            FadisDB.put(key, new FadisString(r + ""));
            return LongNumber.encodeLong(r);
        }
    }

    public static final class incrbyfloat implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            String incrementStr = frame.getArg(2);

            double increment = 0;

            try {
                increment = Double.parseDouble(incrementStr);
            }catch (Exception e){
                throw new FadisException(4002, increment + " 无法转为浮点");
            }

            double r = FadisDB.tryToGetOrCreateDouble(key, true);
            r += increment;
            FadisDB.put(key, new FadisString(r + ""));
            return BulkString.encodedBulkString(r + "");
        }
    }

    public static final class decr implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            double increment = 1;

            long r = FadisDB.tryToGetOrCreateLong(key, true);
            r -= increment;
            FadisDB.put(key, new FadisString(r + ""));
            return LongNumber.encodeLong(r);
        }
    }

    public static final class decrby implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            String key = frame.getArg(1);
            String incrementStr = frame.getArg(2);

            long increment = 0;

            try {
                increment = Long.parseLong(incrementStr);
            }catch (Exception e){
                throw new FadisException(4002, increment + " 无法转为long");
            }

            long r = FadisDB.tryToGetOrCreateLong(key, true);
            r -= increment;
            FadisDB.put(key, new FadisString(r + ""));
            return LongNumber.encodeLong(r);

        }
    }

    public static final class mset implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {

            Map<String, String> map = new HashMap<>();

            String t_key = null;
            for (int i = 1; i < frame.getArgsCount(); i++) {
                if(t_key == null) {
                    t_key = frame.getArg(i);
                } else{
                    map.put(t_key, frame.getArg(i));
                    t_key = null;
                }
            }

            for (String k: map.keySet()){
                ExpireMgmr.deleteExpireInfo(k);
                FadisDB.put(k, new FadisString(map.get(k)));
            }

            return SimpleString.encodedSimpleString("OK");
        }
    }

    public static final class msetnx implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            Map<String, String> map = new HashMap<>();

            String t_key = null;
            for (int i = 1; i < frame.getArgsCount(); i++) {
                if(t_key == null) {
                    t_key = frame.getArg(i);
                } else{
                    map.put(t_key, frame.getArg(i));
                    t_key = null;
                }
            }

            for (String key: map.keySet()){
                if(FadisDB.containsKey(key)){
                    return LongNumber.encodeLong(0);
                }
            }

            for (String k: map.keySet()){
                ExpireMgmr.deleteExpireInfo(k);
                FadisDB.put(k, new FadisString(map.get(k)));
            }
            return LongNumber.encodeLong(1);


        }
    }

    public static final class mget implements CmdHandler{
        @Override
        public String run(FadisActionFrame frame) throws FadisException {
            List<String> keys = new ArrayList<>();
            for (int i = 1; i < frame.getArgsCount(); i++) {
                keys.add(frame.getArg(i));
            }
            List<String> values = new ArrayList<>();
            for (int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);

                if(FadisDB.isString(key)){
                    FadisString r = FadisDB.tryToGetOrCreateString(key, false);
                    values.add(((FadisString)r).getStr());
                }else{
                    values.add(null);
                }
            }

            return CmdEncoder.getDefault().encode(RespArray.from(values));

        }
    }
}
